package com.acgist.snail.context;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;

import com.acgist.snail.config.DownloadConfig;
import com.acgist.snail.context.entity.TaskEntity;
import com.acgist.snail.context.session.TaskSession;
import com.acgist.snail.gui.GuiContext;
import com.acgist.snail.logger.Logger;
import com.acgist.snail.logger.LoggerFactory;
import com.acgist.snail.net.DownloadException;
import com.acgist.snail.protocol.ProtocolContext;
import com.acgist.snail.utils.CollectionUtils;

/**
 * 任务上下文
 * 
 * @author acgist
 */
public final class TaskContext implements IContext {

    private static final Logger LOGGER = LoggerFactory.getLogger(TaskContext.class);
    
    /**
     * 任务队列
     */
    private final List<ITaskSession> tasks;
    /**
     * 下载器线程池
     */
    private final ExecutorService executor;

    private static final TaskContext INSTANCE = new TaskContext();
    
    public static final TaskContext getInstance() {
        return INSTANCE;
    }
    
    private TaskContext() {
        this.tasks    = new ArrayList<>(DownloadConfig.getSize());
        this.executor = SystemThreadContext.newCacheExecutor(0, 60L, SystemThreadContext.SNAIL_THREAD_DOWNLOADER);
    }
    
    /**
     * 新建下载任务
     * 添加下载任务同时开始下载
     * 
     * @param url 下载链接
     * 
     * @return 下载任务
     * 
     * @throws DownloadException 下载异常
     */
    public ITaskSession download(String url) throws DownloadException {
        final ITaskSession session = ProtocolContext.getInstance().buildTaskSession(url);
        session.start();
        return session;
    }
    
    /**
     * 添加下载任务
     * 只添加下载任务不修改任务状态
     * 
     * @param taskSession 任务信息
     * 
     * @throws DownloadException 下载异常
     */
    public void submit(ITaskSession taskSession) throws DownloadException {
        if(taskSession == null) {
            throw new DownloadException("任务信息为空");
        }
        if(ProtocolContext.getInstance().available()) {
            synchronized (this.tasks) {
                // 任务添加必须新建下载器
                taskSession.buildDownloader();
                if(this.tasks.contains(taskSession)) {
                    LOGGER.debug("任务已经存在：{}", taskSession);
                } else {
                    this.tasks.add(taskSession);
                }
            }
            GuiContext.getInstance().refreshTaskList();
        } else {
            throw new DownloadException("下载协议未初始化");
        }
    }
    
    /**
     * 删除下载任务
     * 
     * @param taskSession 下载任务
     */
    public void remove(ITaskSession taskSession) {
        synchronized (this.tasks) {
            LOGGER.debug("删除下载任务：{}", taskSession);
            this.tasks.remove(taskSession);
        }
        GuiContext.getInstance().refreshTaskList();
    }
    
    /**
     * @return 所有下载任务列表
     */
    public List<ITaskSession> allTask() {
        synchronized (this.tasks) {
            return this.tasks.stream()
                .toList();
        }
    }
    
    /**
     * @return 是否含有下载任务
     */
    public boolean running() {
        synchronized (this.tasks) {
            return this.tasks.stream()
                .anyMatch(ITaskSession::statusRunning);
        }
    }
    
    /**
     * 刷新下载任务
     */
    public void refresh() {
        synchronized (this.tasks) {
            // 当前正在下载任务数量
            final long downloadCount = this.tasks.stream()
                .filter(ITaskSession::statusDownload)
                .count();
            final int downloadSize = DownloadConfig.getSize();
            if(downloadCount == downloadSize) {
                LOGGER.debug("下载任务数量正常：{} - {}", downloadSize, downloadCount);
            } else if(downloadCount > downloadSize) {
                LOGGER.debug("暂停部分下载任务：{} - {}", downloadSize, downloadCount);
                this.tasks.stream()
                    .filter(ITaskSession::statusDownload)
                    .skip(downloadSize)
                    .forEach(ITaskSession::await);
            } else {
                LOGGER.debug("开始部分下载任务：{} - {}", downloadSize, downloadCount);
                this.tasks.stream()
                    .filter(ITaskSession::statusAwait)
                    .limit(downloadSize - downloadCount)
                    .map(ITaskSession::getDownloader)
                    .forEach(this.executor::submit);
            }
        }
    }

    /**
     * 加载任务实体列表
     */
    public void load() {
        final List<TaskEntity> list = EntityContext.getInstance().allTask();
        if(CollectionUtils.isNotEmpty(list)) {
            list.forEach(this::load);
            this.refresh();
        }
    }
    
    /**
     * 加载任务实体
     * 
     * @param entity 任务实体
     */
    private void load(TaskEntity entity) {
        try {
            final ITaskSession taskSession = TaskSession.newInstance(entity);
            // 重置状态
            taskSession.reset();
            this.submit(taskSession);
        } catch (Exception e) {
            LOGGER.error("添加下载任务异常：{}", entity, e);
            EntityContext.getInstance().deleteTask(entity);
        }
    }
    
    /**
     * 关闭任务上下文
     */
    public void shutdown() {
        LOGGER.debug("关闭任务上下文");
        // 暂停所有任务
        synchronized (this.tasks) {
            this.tasks.stream()
                .filter(ITaskSession::statusRunning)
                .forEach(ITaskSession::pause);
        }
        SystemThreadContext.shutdown(this.executor);
    }

}
